# akshare_finance/dupont.py
from typing import Optional, List, Dict
import pandas as pd
import akshare as ak
from akshare_finance.fetcher import FinancialDataFetcher

class DupontAnalyzer:
    """杜邦分析：基础版和改进版"""
    
    def __init__(self, top_n: int = 8, fetcher: Optional[FinancialDataFetcher] = None):
        self.top_n = top_n
        self.fetcher = fetcher

    def _get_data(self, symbol: str, data_type: str = "abstract", **kwargs) -> pd.DataFrame:
        """统一数据获取接口"""
        try:
            if self.fetcher:
                if data_type == "abstract":
                    return self.fetcher.get_financial_abstract(symbol)
                elif data_type == "report":
                    return self.fetcher.get_financial_report_sina(symbol, kwargs.get("which", ""))
            else:
                if data_type == "abstract":
                    return ak.stock_financial_abstract_ths(symbol=symbol, indicator="按报告期")
                elif data_type == "report":
                    return ak.stock_financial_report_sina(stock=symbol, symbol=kwargs.get("which", ""))
        except Exception:
            pass
        return pd.DataFrame()

    def _find_metric(self, df: pd.DataFrame, metric_names: List[str]) -> Optional[str]:
        """在指标列中查找匹配的指标名"""
        if df.empty or "指标" not in df.columns:
            return None
            
        indicator_col = df["指标"].astype(str)
        for name in metric_names:
            matches = [x for x in indicator_col.unique() if name in str(x)]
            if matches:
                return sorted(matches, key=len, reverse=True)[0]
        return None

    def compute_basic_dupont(self, symbol: str) -> pd.DataFrame:
        """基础杜邦分析：ROE = 销售净利率 × 总资产周转率 × 权益乘数
        适配东方财富API格式，提供完整的杜邦分析指标"""
        df = self._get_data(symbol, "abstract")
        if df.empty:
            return pd.DataFrame()

        # 东方财富API列名映射
        column_mapping = {
            "ROEJQ": "ROE(%)",
            "XSJLL": "销售净利率(%)",
            "TOAZZL": "总资产周转率(次)", 
            "ZCFZL": "资产负债率(%)",
            "QYCS": "权益乘数"
        }
        
        # 检查必要的列是否存在
        available_cols = []
        for api_col, std_col in column_mapping.items():
            if api_col in df.columns:
                available_cols.append((api_col, std_col))
        
        if len(available_cols) < 3:  # 至少需要3个指标进行杜邦分析
            return pd.DataFrame()

        # 确定时间列
        time_col = 'REPORT_DATE' if 'REPORT_DATE' in df.columns else '报告期'
        
        # 提取最近几期数据
        df_recent = df.head(self.top_n).copy()
        
        # 转换数据格式
        result_data = {}
        
        # 处理时间列
        if time_col in df_recent.columns:
            result_data["报告期"] = pd.to_datetime(df_recent[time_col]).dt.strftime('%Y%m%d').tolist()
        
        # 处理各指标数据
        for api_col, std_col in available_cols:
            if api_col in df_recent.columns:
                # 直接使用数值，东方财富API返回的就是数值型数据
                result_data[std_col] = df_recent[api_col].tolist()

        result = pd.DataFrame(result_data)
        result.set_index("报告期", inplace=True)
        
        # 计算标准杜邦分解：ROE = 销售净利率 × 总资产周转率 × 权益乘数
        required_cols = ["销售净利率(%)", "总资产周转率(次)", "权益乘数"]
        if all(col in result.columns for col in required_cols):
            result["杜邦ROE(%)_计算"] = (
                result["销售净利率(%)"] / 100.0 *
                result["总资产周转率(次)"] *
                result["权益乘数"] * 100.0
            )
            
            if "ROE(%)" in result.columns:
                result["ROE差值(百分点)"] = result["杜邦ROE(%)_计算"] - result["ROE(%)"]
                
        # 如果没有直接的权益乘数，但有资产负债率，则计算权益乘数
        elif "资产负债率(%)" in result.columns and "权益乘数" not in result.columns:
            asset_liability_ratio = result["资产负债率(%)"].fillna(0).clip(upper=99.9)
            result["权益乘数_计算"] = 1.0 / (1.0 - asset_liability_ratio / 100.0)
            
            if all(col in result.columns for col in ["销售净利率(%)", "总资产周转率(次)"]):
                result["杜邦ROE(%)_计算"] = (
                    result["销售净利率(%)"] / 100.0 *
                    result["总资产周转率(次)"] *
                    result["权益乘数_计算"] * 100.0
                )
                
                if "ROE(%)" in result.columns:
                    result["ROE差值(百分点)"] = result["杜邦ROE(%)_计算"] - result["ROE(%)"]

        return result.dropna(how='all', axis=1)

    def _parse_financial_sheet(self, df: pd.DataFrame) -> pd.DataFrame:
        """解析财务报表数据格式"""
        if df.empty:
            return df
            
        # 处理不同的表格格式
        if "项目" in df.columns or "科目" in df.columns:
            item_col = "项目" if "项目" in df.columns else "科目"
            date_cols = [c for c in df.columns if str(c).isdigit()]
            result = df[[item_col] + date_cols].copy()
            result.rename(columns={item_col: "项目"}, inplace=True)
            return result.drop_duplicates(subset=["项目"])
            
        elif "报告日" in df.columns:
            # 转置处理
            df_copy = df.copy()
            df_copy["报告日"] = df_copy["报告日"].astype(str).str.replace(r"[-/]", "", regex=True)
            transposed = df_copy.set_index("报告日").T.reset_index()
            transposed.rename(columns={"index": "项目"}, inplace=True)
            
            date_cols = [c for c in transposed.columns if str(c).isdigit()]
            result = transposed[["项目"] + date_cols].copy()
            
            # 数值清理
            for col in date_cols:
                result[col] = pd.to_numeric(
                    result[col].astype(str).str.replace(",", ""), errors="coerce"
                )
            return result.drop_duplicates(subset=["项目"])
            
        return df

    def _find_item_value(self, df: pd.DataFrame, item_names: List[str]) -> pd.Series:
        """在财务报表中查找指定项目的值"""
        if df.empty or "项目" not in df.columns:
            return pd.Series(dtype="float64")
            
        date_cols = [c for c in df.columns if str(c).isdigit()]
        if not date_cols:
            return pd.Series(dtype="float64")
            
        for item_name in item_names:
            matches = df[df["项目"].str.contains(item_name, na=False)]
            if not matches.empty:
                series = matches.iloc[0][date_cols]
                return pd.to_numeric(series.astype(str).str.replace(",", ""), errors="coerce")
                
        return pd.Series(index=date_cols, dtype="float64")

    def compute_advanced_dupont(self, symbol: str) -> pd.DataFrame:
        """改进杜邦分析：ROE = RNOA + (RNOA - NBC) × FLEV"""
        # 获取报表数据
        bs_raw = self._get_data(symbol, "report", which="资产负债表")
        is_raw = self._get_data(symbol, "report", which="利润表")
        
        if bs_raw.empty or is_raw.empty:
            return pd.DataFrame()
            
        bs = self._parse_financial_sheet(bs_raw)
        is_ = self._parse_financial_sheet(is_raw)
        
        date_cols = [c for c in bs.columns if str(c).isdigit()]
        if not date_cols:
            return pd.DataFrame()
            
        date_cols_sorted = sorted(date_cols, reverse=True)

        # 关键项目映射
        key_items = {
            "总资产": ["资产总计", "总资产"],
            "总负债": ["负债合计", "总负债"],
            "归母权益": ["归属于母公司股东权益合计", "股东权益合计"],
            "货币资金": ["货币资金"],
            "短期借款": ["短期借款"],
            "长期借款": ["长期借款"],
            "营业利润": ["营业利润"],
            "利润总额": ["利润总额"],
            "所得税费用": ["所得税费用"],
            "财务费用": ["财务费用"]
        }

        # 提取关键数据
        data = {}
        for key, item_names in key_items.items():
            if key in ["营业利润", "利润总额", "所得税费用", "财务费用"]:
                data[key] = self._find_item_value(is_, item_names)
            else:
                data[key] = self._find_item_value(bs, item_names)

        # 简化计算（避免过于复杂的金融资产负债分类）
        total_assets = data["总资产"]
        total_liab = data["总负债"]
        equity = data["归母权益"]
        cash = data["货币资金"]
        debt = data["短期借款"] + data["长期借款"]
        
        ebit = data["营业利润"]
        pbt = data["利润总额"]
        tax = data["所得税费用"]
        fin_cost = data["财务费用"]

        # 计算关键比率
        tax_rate = (tax / pbt).fillna(0).clip(0, 1)
        net_debt = debt - cash
        noa = total_assets - cash - (total_liab - debt)  # 简化的净经营资产
        
        # 期间平均
        avg_noa = (noa + noa.shift(-1)) / 2.0
        avg_net_debt = (net_debt + net_debt.shift(-1)) / 2.0
        avg_equity = (equity + equity.shift(-1)) / 2.0

        # 杜邦分解
        nopat = ebit * (1 - tax_rate)
        rnoa = (nopat / avg_noa * 100.0).replace([float('inf'), -float('inf')], 0)
        nbc = (fin_cost * (1 - tax_rate) / avg_net_debt * 100.0).replace([float('inf'), -float('inf')], 0)
        flev = (avg_net_debt / avg_equity).replace([float('inf'), -float('inf')], 0)
        roe_advanced = rnoa + (rnoa - nbc) * flev

        result = pd.DataFrame({
            "总资产": total_assets,
            "归母权益": equity,
            "净负债": net_debt,
            "NOA": noa,
            "EBIT": ebit,
            "有效税率(%)": tax_rate * 100,
            "RNOA(%)": rnoa,
            "NBC(%)": nbc,
            "FLEV": flev,
            "ROE_改进(%)": roe_advanced
        }, index=date_cols_sorted)
        
        result.index.name = "报告期"
        return result.head(self.top_n).fillna(0)